﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using com.opttek.optquest;

namespace ConsoleApplication1
{

    class VariableCube
    {

        public Dictionary<String,
            Dictionary<String, Dictionary<int, DesignVariable>>> machineRefs
            = new Dictionary<string, Dictionary<string, Dictionary<int, DesignVariable>>>();

        public Dictionary<String,
    Dictionary<String, Dictionary<int, DesignVariable>>> materialRefs
    = new Dictionary<string, Dictionary<string, Dictionary<int, DesignVariable>>>();

        public Dictionary<String, DesignVariable> designVariables = new Dictionary<string, DesignVariable>();

        public Dictionary<String, Dictionary<int, DesignVariable>> machineDesignInternal =
            new Dictionary<string, Dictionary<int, DesignVariable>>();

        SimData mydata;

        public VariableCube(SimData data)
        {
            mydata = data;
            DesignVariable myvar;
            foreach (Machine machine in mydata.mymachines.Values)
            {
                DesignMachine dmachine = new DesignMachine(machine);
                for (int i = 0; i < mydata.horizon; i++)
                {
                    string key = machine.machine + "_" + i;

                    myvar = new DesignVariable(dmachine, key,i, -1, machine.materials.Count -1, 1);
                    designVariables.Add(key, myvar);
                    

                    string mach_key = machine.machine;

                    if(!machineDesignInternal.ContainsKey(mach_key))
                        machineDesignInternal.Add(mach_key,new Dictionary<int,DesignVariable>());

                    if (!machineDesignInternal[mach_key].ContainsKey(i))
                        machineDesignInternal[mach_key].Add(i, myvar);

                    //Chequea si ya ingrese esta maquina en machineRefs, si no, lo hago
                    if (!machineRefs.ContainsKey(mach_key))
                        machineRefs.Add(mach_key, new Dictionary<string, Dictionary<int, DesignVariable>>());

                    //Design variable will have to be added in all binary slots
                    //machref.machkey ->( matkey -> (timestep -> binvar ) )
                    foreach (Materials mat in machine.materials.Values)
                    {
                        string mat_key = mat.material;

                        //Chequea lo mismo para material
                        if (!materialRefs.ContainsKey(mat_key))
                            materialRefs.Add(mat_key, new Dictionary<string, Dictionary<int, DesignVariable>>());

                        //Chequo que el dic de mat -> dic no sea null
                        if (!machineRefs[mach_key].ContainsKey(mat_key))
                            machineRefs[mach_key].Add(mat_key, new Dictionary<int, DesignVariable>());

                        //Chequeo que el dic de mach->dic no sea null
                        if (!materialRefs[mat_key].ContainsKey(mach_key))
                            materialRefs[mat_key].Add(mach_key, new Dictionary<int, DesignVariable>());

                        machineRefs[mach_key][mat_key].Add(i, myvar);
                        materialRefs[mat_key][mach_key].Add(i, myvar);
                    }
                    
                    

                }

            }
        }

        private List<DesignVariable> getVars(Dictionary<int, DesignVariable> dict, int epoch)
        {
            Epoch myepoch = mydata.myepochs[epoch];
            List<DesignVariable> ans = new List<DesignVariable>();

            foreach (int key in dict.Keys)
            {
                if (key >= myepoch.start && key < (myepoch.start + myepoch.duration))
                    ans.Add(dict[key]);
            }
            return ans;

        }

        private List<DesignVariable> getVarsForI(Dictionary<int, DesignVariable> dict, int i)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            foreach (int key in dict.Keys)
            {
                if (key == i)
                    ans.Add(dict[key]);
            }
            return ans;
        }

        private List<DesignVariable> getVars(Dictionary<int, DesignVariable> dict)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            foreach (int key in dict.Keys)
            {
                ans.Add(dict[key]);
            }
            return ans;
        }

        private List<DesignVariable> getVars(Dictionary<String, Dictionary<int, DesignVariable>> dict)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            foreach (String key in dict.Keys)
            {
                ans.AddRange(getVars(dict[key]));
            }
            return ans;
        }

        private List<DesignVariable> getVars(Dictionary<String, Dictionary<int, DesignVariable>> dict, int epoch)
        {

            List<DesignVariable> ans = new List<DesignVariable>();
            foreach (String key in dict.Keys)
            {
                ans.AddRange(getVars(dict[key], epoch));
            }
            return ans;
        }

        private List<DesignVariable> getVarsForI(Dictionary<String, Dictionary<int, DesignVariable>> dict, int i)
        {

            List<DesignVariable> ans = new List<DesignVariable>();
            foreach (String key in dict.Keys)
            {
                ans.AddRange(getVarsForI(dict[key], i));
            }
            return ans;
        }


        public List<DesignVariable> getVarsForMachineMaterial(String machkey, String matkey)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            ans.AddRange(getVars(machineRefs[machkey][matkey]));

            return ans;
        }

        public List<DesignVariable> getVarsForMachine(String machkey)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            foreach(int k in machineDesignInternal[machkey].Keys )
            {
                ans.Add(machineDesignInternal[machkey][k]);
            }
            return ans;

        }

        public List<DesignVariable> getVarsForMaterial(String matkey)
        {
            try
            {
                return getVars(materialRefs[matkey]).ToList();
            }
            catch { 
                //If there are materials that are not produced by anyone, just return
                //an empty list
                return new List<DesignVariable>();
            }
        }

        public List<DesignVariable> getVarsForMachine(String machkey, int epoch)
        {
            List<DesignVariable> ans = new List<DesignVariable>();
            Epoch e = mydata.myepochs[epoch];
            foreach (int k in machineDesignInternal[machkey].Keys)
            {
                
                if( k > e.start && k < e.start + e.duration )
                    ans.Add(machineDesignInternal[machkey][k]);
            }
            return ans;

        }

        public List<DesignVariable> getVarsForMaterial(String matkey, int epoch)
        {
            try
            {
                return getVars(materialRefs[matkey], epoch).ToList();
            }
            catch (Exception)
            {
                return new List<DesignVariable>();
            }
        }


        public List<DesignVariable> getVarsForMaterial_i(String matkey, int i)
        {
            try
            {
                return getVarsForI(materialRefs[matkey], i).ToList();
            }
            catch (Exception)
            {
                return new List<DesignVariable>();
            }
        }

        public String describeYourself()
        {
            String output = "I am CUBE.\n I have " + machineRefs.Count + " machines, " +
                materialRefs.Count + " materials and " + designVariables.Count + " binary variables";
            return output;
        }

    }

}
